export namespace TypesMouseEvent {
	export interface callback {
		(e: MouseEvent): void
	}
}

export class MouseService {
	public x = 0
	public y = 0
	public activeX = 0
	public activeY = 0
	public moveCallbackMap: Map<string, TypesMouseEvent.callback> = new Map()
	public upCallbackList: TypesMouseEvent.callback[] = []
	public upCaptureCallbackList: TypesMouseEvent.callback[] = []
	public downCallbackList: TypesMouseEvent.callback[] = []
	public downCaptureCallbackList: TypesMouseEvent.callback[] = []
	public constructor() {}
	setXY(x: number, y: number) {
		this.x = x
		this.y = y
	}
	setActiveXY(x: number, y: number) {
		this.x = x
		this.y = y
		this.activeX = x
		this.activeY = y
	}
	addMoveCallback(id: string, cb: TypesMouseEvent.callback) {
		this.moveCallbackMap.set(id, cb)
	}
	deleteMoveCallback(id: string) {
		this.moveCallbackMap.delete(id)
	}
	runMoveCallbacks(e: MouseEvent) {
		const callbacks = [...this.moveCallbackMap.values()]
		requestAnimationFrame(() => callbacks.forEach((cb) => cb(e)))
	}
	addUpCallback(cb: TypesMouseEvent.callback) {
		this.upCallbackList.push(cb)
	}
	runUpCallback(e: MouseEvent) {
		while (this.upCallbackList.length) this.upCallbackList.pop()?.(e)
	}
	addCaptureUpCallback(cb: TypesMouseEvent.callback) {
		this.upCaptureCallbackList.push(cb)
	}
	runCaptureUpCallback(e: MouseEvent) {
		while (this.upCaptureCallbackList.length) this.upCallbackList.pop()?.(e)
	}
	addDownCallback(cb: TypesMouseEvent.callback) {
		this.downCallbackList.push(cb)
	}
	runDownCallback(e: MouseEvent) {
		while (this.downCallbackList.length) this.downCallbackList.pop()?.(e)
	}
	addCaptureDownCallback(cb: TypesMouseEvent.callback) {
		this.downCaptureCallbackList.push(cb)
	}
	runCaptureDownCallback(e: MouseEvent) {
		while (this.downCaptureCallbackList.length) this.downCallbackList.pop()?.(e)
	}
}

export const initMouseService = (Mouse: MouseService) => {
	addEventListener(
		'mousemove',
		(e) => {
			Mouse.setXY(e.clientX, e.clientY)
			Mouse.runMoveCallbacks(e)
		},
		{ capture: true }
	)

	addEventListener('mouseup', (e) => {
		Mouse.setActiveXY(e.clientX, e.clientY)
		Mouse.runUpCallback(e)
	})

	addEventListener(
		'mouseup',
		(e) => {
			Mouse.setActiveXY(e.clientX, e.clientY)
			Mouse.runCaptureUpCallback(e)
		},
		{ capture: true }
	)

	addEventListener('mousedown', (e) => {
		Mouse.setActiveXY(e.clientX, e.clientY)
		Mouse.runDownCallback(e)
	})

	addEventListener(
		'mousedown',
		(e) => {
			Mouse.setActiveXY(e.clientX, e.clientY)
			Mouse.runCaptureDownCallback(e)
		},
		{ capture: true }
	)

	addEventListener('contextmenu', (e) => Mouse.setActiveXY(e.clientX, e.clientY))

	return Mouse
}

export const Mouse = initMouseService(new MouseService())
